package data;

import java.math.BigInteger;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import com.google.common.collect.Lists;

public class Blind
{
    public Blind()
    {
    }

    public String SecHash( String communicate, RSAPublicKey pub )
    {
        BigInteger ns = pub.getModulus();
        BigInteger es = pub.getPublicExponent();
        String[] parts = communicate.split( "!!--PART--!!" );
        StringBuffer result = new StringBuffer();
        int counter = parts.length;
        for( int i = 1; i < counter; i++ )
        { // tu tez przez to, ze pierwszy jest pusty
            BigInteger _temp = new BigInteger( parts[ i ] );
            String _temp_ = _temp.toString();
            int _count = _temp_.length();
            result.append( "!!--PART--!!" );
            for(int j=0;j<_count/100;j++){
            	result.append("!!--ERROR--!!");
            	String zxc = _temp_.substring(j*100, (j+1)*100);
            	BigInteger _tempp = new BigInteger(zxc.getBytes());
            	BigInteger sechashed = _tempp.modPow(es, ns);
            	result.append(sechashed.toString());
            }
            int check = _count / 100;
            if( check * 100 != _count ){
            	result.append("!!--ERROR--!!");
            	String zxc = _temp_.substring(check*100,_count);
            	BigInteger _tempp = new BigInteger(zxc.getBytes());
            	BigInteger sechashed = _tempp.modPow(es, ns);
            	result.append(sechashed.toString());
            }
        }
        
        return result.toString();
    }

    public String SecUnhash( String communicate, RSAPrivateKey priv )
    {
        BigInteger ns = priv.getModulus();
        BigInteger ds = priv.getPrivateExponent();
        String[] parts = communicate.split( "!!--PART--!!" );
        StringBuffer result = new StringBuffer();
        int counter = parts.length;
        for( int i = 1; i < counter; i++ )
        { // tu tez przez to, ze pierwszy jest pusty
        	result.append( "!!--PART--!!" );
        	String[] kawalki = parts[i].split("!!--ERROR--!!");
        	int _counter = kawalki.length;
        	for(int j=1;j<_counter;j++){
        		BigInteger kawalek = new BigInteger(kawalki[j]);
        		BigInteger unsechashed1 = kawalek.modPow( ds, ns );
        		result.append(new String( unsechashed1.toByteArray()) );
        	}
        }
        return result.toString();
    }

    public String doBlind( VotingSet vs, RSAPublicKey pubKey, List<BigInteger> tab )
    {
        BigInteger n = pubKey.getModulus();
        BigInteger e = pubKey.getPublicExponent();
        StringBuffer communicate = new StringBuffer();
        List<VotingCard> _cards = vs.getCards();
        int size = _cards.size();
        for( int i = 0; i < size; i++ )
        {
            VotingCard vc = _cards.get( i );
            communicate.append( "!!--VC--!!" );
            communicate.append( vc.toString() );
        }
        BigInteger r = null;
        boolean found = false;
        SecureRandom random = null;
        try
        {
            random = SecureRandom.getInstance( "SHA1PRNG", "SUN" );
        }
        catch( Exception e1 )
        {
            e1.printStackTrace();
        }
        byte[] randomBytes = new byte[ 10 ];
        while( !found )
        {
            random.nextBytes( randomBytes );
            r = new BigInteger( 1, randomBytes );
            BigInteger gcd = r.gcd( n );
            if( gcd.equals( BigInteger.ONE ) )
            {
                found = true;
                tab.add( r );
            }
        }
        StringBuffer result = new StringBuffer();
        String _communicate = communicate.toString();
        String[] cards = _communicate.split( "!!--VC--!!" );
        int amount = cards.length;
        for( int i = 1; i < amount; i++ )
        { // pierwszy jest pusty, ehh...
            result.append( "!!--VC--!!" );
            int _size = cards[ i ].length();
            for( int j = 0; j < ( _size / 100 ); j++ )
            {
                String temp = cards[ i ].substring( ( j * 100 ), ( ( j + 1 ) * 100 ) );
                byte[] tempByte = temp.getBytes();
                BigInteger m = new BigInteger( tempByte );
                BigInteger mprim = m.multiply( r.modPow( e, n ) ).mod( n );
                String part = mprim.toString();
                result.append( "!!--PART--!!" );
                result.append( part );
            }
            int check = _size / 100;
            if( check * 100 != _size )
            {
                String temp = cards[ i ].substring( ( check * 100 ), _size );
                byte[] tempByte = temp.getBytes();
                BigInteger m = new BigInteger( tempByte );
                BigInteger mprim = m.multiply( r.modPow( e, n ) ).mod( n );
                String part = mprim.toString();
                result.append( "!!--PART--!!" );
                result.append( part );
            }
        }
        return result.toString();
    }

    public String giveBlindKeys( int id, List<BigInteger> tab )
    { // id - jedyny, ktorego nie chcemy
        int counter = tab.size();
        StringBuffer result = new StringBuffer();
        for( int i = 0; i < counter; i++ )
        {
            if( i == id )
            {
            }
            else
            {
                result.append( "!!--ALOHA--!!" );
                result.append( tab.get( i ) );
            }
        }
        return result.toString();
    }

    public BigInteger[] getBlindKeys( String communicate )
    {
        BigInteger[] result = new BigInteger[ communicate.length() ];
        String[] parts = communicate.split( "!!--ALOHA--!!" );
        int counter = parts.length;
        for( int i = 1; i < counter; i++ )
        {
            result[ i - 1 ] = new BigInteger( parts[ i ] );
        }
        return result;
    }

    public String sign( String communicate, RSAPrivateKey privKey )
    {
        BigInteger d = privKey.getPrivateExponent();
        BigInteger n = privKey.getModulus();
        String[] cards = communicate.split( "!!--VC--!!" );
        StringBuffer result = new StringBuffer();
        int counter = cards.length;
        for( int i = 1; i < counter; i++ )
        { // tu tez przez to, ze pierwszy jest pusty
            result.append( "!!--VC--!!" );
            String[] parts = cards[ i ].split( "!!--PART--!!" );
            int partsCounter = parts.length;
            for( int j = 1; j < partsCounter; j++ )
            {
                result.append( "!!--PART--!!" );
                BigInteger _temp = new BigInteger( parts[ j ] ); // spowrotem na
                                                                 // bigInt
                BigInteger signed = _temp.modPow( d, n );
                result.append( signed.toString() );
            }
        }
        return result.toString();
    }

    public String unblind( String communicate, int id, RSAPublicKey pubKey, List<BigInteger> tab )
    {
        BigInteger n = pubKey.getModulus();
        BigInteger r = tab.get( id );
        String[] cards = communicate.split( "!!--VC--!!" );
        StringBuffer result = new StringBuffer();
        int counter = cards.length;
        for( int i = 1; i < counter; i++ )
        { // tu tez przez to, ze pierwszy jest pusty
            result.append( "!!--VC--!!" );
            String[] parts = cards[ i ].split( "!!--PART--!!" );
            int partsCounter = parts.length;
            for( int j = 1; j < partsCounter; j++ )
            {
                result.append( "!!--PART--!!" );
                BigInteger _temp = new BigInteger( parts[ j ] );
                BigInteger unblinded = _temp.multiply( r.modInverse( n ) ).mod( n );
                result.append( unblinded.toString() );
            }
        }
        return result.toString();
    }

    public String unblind( String communicate, RSAPublicKey pubKey, BigInteger blindParameter )
    {
        LinkedList<BigInteger> toSend = Lists.newLinkedList();
        toSend.add( blindParameter );
        return unblind( communicate, 0, pubKey, toSend );
    }

    public String pickCard( String communicate, int num )
    {
        try
        {
            String[] cards = communicate.split( "!!--VC--!!" );
            return cards[ num + 1 ];
        }
        catch( Exception e2 )
        {
            throw new IllegalArgumentException( e2 );
        }
    }

    public VotingSet unsignSet( String communicate, RSAPublicKey _pk )
    {
        VotingSet set = new VotingSet();
        String[] cards = communicate.split( "!!--VC--!!" );
        cards = Arrays.copyOfRange( cards, 1, cards.length );
        for( String card: cards )
            set.addCard( unsign( card, _pk ) );
        return set;
    }

    public VotingCard unsign( String communicate, RSAPublicKey _pk )
    {
        BigInteger n = _pk.getModulus();
        BigInteger e = _pk.getPublicExponent();
        String[] parts = communicate.split( "!!--PART--!!" );
        StringBuffer result = new StringBuffer();
        int counter = parts.length;
        for( int i = 1; i < counter; i++ )
        { // tu tez przez to, ze pierwszy jest pusty
            parts[ i ] = parts[ i ].replace( "!!--VC--!!", "" );
            BigInteger _temp = new BigInteger( parts[ i ] );
            BigInteger unblinded = _temp.modPow( e, n );
            byte[] byteArray = unblinded.toByteArray();
            String s2 = new String( byteArray );
            result.append( s2 );
        }
        VotingCard vc = new VotingCard( result.toString() );
        return vc;
    }
}
